# encoding = utf-8
import datetime
import pickle

import pandas as pd
import xgboost as xgb
from sklearn.model_selection import train_test_split

from application.logging import logger
from application.model.v3_model.CV_parameter import CV_parameter
from application.model.v3_model.cookie_students.function_cookie_student import function_cookie_student
from application.model.v3_model.mape import mape
from application.utils.CodeTimingUtil import CodeTimingUtil
from application.utils.ModelStorePathUtil import ModelStorePathUtil
from application.utils.MySQLUtils import MySQLUtils


@CodeTimingUtil(name="[瞬热模型]v3modeltype0")
def v3modeltype0(write_list: list, model_name, Id, start_time, end_time, create_or_update):
    """
    瞬热模型:创建、更新
    :param write_list:
    :param model_name:
    :param Id:
    :param start_time:
    :param end_time:
    :param create_or_update:
    :return:
    """
    # 查询语句
    sql_sentence = f"SELECT * FROM sn_control_model_station_instant_transition " \
                   f"WHERE station_id= '{Id}' AND collect_time BETWEEN '{start_time}' AND '{end_time}'"
    logger.info(f"查询语句[瞬热历史数据]: {sql_sentence}")
    #
    json_code = {}
    # try: ->
    try:
        # 获取连接
        con = MySQLUtils.get_mysql_connection()
        # 查询数据
        logger.info(f"开始查询:\n {sql_sentence}")
        df = pd.read_sql_query(sql=sql_sentence, con=con)
        # 查询结果
        logger.info(f"查询结果:\n {df}")
        # 数据过滤:字段
        data = df[[
            "outside_temper",
            "outside_weather",
            "outside_humidity",
            "outside_wind",
            "average_inside_temper",
            "instant_heat"]]
        # 过滤结果
        logger.info(f"过滤结果[字段]:\n {data}")
        # 数据过滤:瞬热 >0
        data = data[data["instant_heat"] > 0]
        data = data.reset_index(drop=True)
        # 过滤结果
        logger.info(f"过滤结果[瞬热>0]:\n {data}")
        # 数据集为空
        if len(data) == 0:
            raise BaseException("没有可用的数据集")
            pass
        # 训练数据集:train_x,train_y
        train_x = data.iloc[:, :-1]
        train_y = data.iloc[:, -1:]
        # 打印数据
        logger.info(f"训练集[train_x]:\n {train_x}")
        logger.info(f"训练集[train_y]:\n {train_y}")
        #
        logger.info(f"")
        # 学生化残差计算
        union, intersection = function_cookie_student(train_x, train_y, train_x, train_y)
        logger.info(f"学生化残差结果[unit][{len(union)}]: \n {union}")
        logger.info(f"学生化残差结果[intersection][{len(intersection)}]:\n {intersection}")
        #
        data = data.drop(index=union)
        data = data.reset_index(drop=True)
        #
        data = data[[
            "outside_temper",
            "outside_weather",
            "outside_humidity",
            "outside_wind",
            "average_inside_temper",
            "instant_heat"]]
        #
        data_x = data.iloc[:, :-1]
        data_y = data.iloc[:, -1:]
        #
        train_x, test_x, train_y, test_y = train_test_split(data_x, data_y, test_size=0.2, random_state=42)
        # 调参开始
        best_param = CV_parameter(train_x, train_y)
        # 调参结果
        n_estimators = best_param.get("n_estimators")
        max_depth = best_param.get("max_depth")
        min_child_weight = best_param.get("min_child_weight")
        gamma = best_param.get("gamma")
        subsample = best_param.get("subsample")
        colsample_bytree = best_param.get("colsample_bytree")
        reg_alpha = best_param.get("reg_alpha")
        reg_lambda = best_param.get("reg_lambda")
        learning_rate = best_param.get("learning_rate")
        # 初始化模型
        model = xgb.XGBRegressor(
            n_estimators=n_estimators,
            max_depth=max_depth,
            min_child_weight=min_child_weight,
            seed=0,
            subsample=subsample,
            colsample_bytree=colsample_bytree,
            gamma=gamma,
            reg_alpha=reg_alpha,
            reg_lambda=reg_lambda,
            learning_rate=learning_rate
        )
        # 训练模型
        timer = CodeTimingUtil.timer_start(name="[训练模型]model.fit(train_x, train_y)")
        logger.info(f"训练模型:开始")
        model.fit(train_x, train_y)
        logger.info(f"训练模型:完成")
        CodeTimingUtil.timer_stop(timer=timer)
        #
        y_pre = model.predict(test_x)
        #
        logger.info(f"模型预测数据: {y_pre}")
        # 模型打分:R2(The best possible score is 1.0)
        R2 = model.score(test_x, test_y)
        #
        logger.warning(f"模型评分[R2]: {R2}")
        #
        MAPE = mape(list(test_y["instant_heat"]), y_pre)
        #
        logger.warning(f"模型评分[MAPE]: {MAPE}")
        # if ->
        if R2 >= 0.1:
            json_code["Useful"] = 0
            pass  # <- if
        else:  # else ->
            json_code["Useful"] = 1
            pass  # <- else
        # TODO 重新训练:使用全部数据
        # 模型名称
        model_name = str(model_name)
        logger.info(f"模型名称: {model_name}")
        # 模型存储路径
        model_name = ModelStorePathUtil.get_model_path_by_model_name_type0(model_name=model_name)
        logger.info(f"模型存储路径: {model_name}")
        #
        pickle.dump(model, open(model_name, "wb"))
        #
        Time = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
        #
        con = MySQLUtils.get_mysql_connection()
        cur = con.cursor()
        #
        # if ->
        if create_or_update == "create":
            sql = f"UPDATE sn_control_hotstation_model SET is_created=1 ,last_model_update_time= '{Time}' WHERE model_type=0 AND station_id ='{Id}'"
            pass  # <- if
        elif create_or_update == "update":  # else ->
            sql = f"UPDATE sn_control_hotstation_model SET is_created=1 ,last_model_update_time= '{Time}' WHERE model_type=0 AND station_id ='{Id}'"
            pass  # <- else
        # 更新语句
        logger.info(f"更新语句: {sql}")
        #
        cur.execute(sql)
        #
        con.commit()
        cur.close()
        con.close()
        #
        logger.info(f"模型存储完毕: {model_name}")
        #
        json_code["Model_status"] = 0
        json_code["Return_state_code"] = 0
        #
        write_list.append(json_code)
        #
        logger.info(f"返回结果: \n {write_list}")
        #
        return write_list
        pass  # <- try
    except BaseException as err:  # except ->
        logger.error(f"模型存储失败:\n {err}")
        #
        json_code["Model_status"] = -1
        json_code["Return_state_code"] = -5
        json_code["Return_message"] = f"{err}"
        write_list.append(json_code)
        #
        logger.info(f"返回结果: \n {write_list}")
        return write_list
        pass  # <- except
    pass


pass

if __name__ == '__main__':
    pass
